package com.jenkin.jcache.realize;

import java.util.Calendar;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import com.jenkin.jcache.base.IMapCache;

/**
 * 哈希表缓存
 * @author yangjie
 *
 * @param <K>
 * @param <V>
 */
public class MapCache<K, V> implements IMapCache<K, V> {
	
	private volatile ConcurrentHashMap<K, V> concurrentHashMap = null;
	
	private int expirationSeconds = 0;
	
	private volatile Calendar expirationTime;
	
	private FillListener fillListener;
	
	MapCache(ConcurrentHashMap<K, V> concurrentHashMap, int expirationSeconds, Calendar expirationTime){
		this.concurrentHashMap = concurrentHashMap;
		this.expirationSeconds = expirationSeconds;
		this.expirationTime = expirationTime;
	}
	
	/**
	 * 根据key得到value，如果key不存在返回null，会进行缓存过期检查，如果缓存已过期就调用用户自定义的填充数据方法
	 */
	public synchronized V get(K key) {
		isPastDue();
		if (concurrentHashMap.containsKey(key)) {
			return concurrentHashMap.get(key);
		}else {
			return null;
		}
	}
	
	/**
	 * 得到缓存大小，会进行缓存过期检查，如果缓存已过期就调用用户自定义的填充数据方法
	 */
	public int size() {
		isPastDue();
		return concurrentHashMap.size();
	}
	
	/**
	 * 是否包含key，会进行缓存过期检查，如果缓存已过期就调用用户自定义的填充数据方法
	 */
	public boolean containsKey(K key) {
		isPastDue();
		return concurrentHashMap.containsKey(key);
	}
	
	/**
	 * 添加缓存数据，不会进行缓存过期检查
	 */
	public V add(K key, V value) {
		return concurrentHashMap.put(key, value);
	}
	
	/**
	 * 移除缓存数据，不会进行缓存过期检查
	 */
	public V remove(K key) {
		if (concurrentHashMap.containsKey(key)) {
			return concurrentHashMap.remove(key);
		}
		return null;
	}
	
	/**
	 * 清空缓存，不会进行缓存过期检查
	 */
	public void clear() {
		concurrentHashMap.clear();
		
	}
	
	/**
	 * 得到迭代器
	 * @return
	 */
	public Iterator<Entry<K, V>> iterator () {
		return concurrentHashMap.entrySet().iterator();
	}
	
	/**
	 * 添加填充数据的方法
	 * @param fillListener
	 */
	public void addFillListener(FillListener fillListener) {
		this.fillListener = fillListener;
	}
	
	/**
	 * 缓存是否已过期，如果过期就调用用户自定义的填充数据方法
	 */
	private void isPastDue() {
		if (expirationSeconds != 0 && expirationTime.before(Calendar.getInstance())) {
			synchronized(this) {
				if (expirationTime.before(Calendar.getInstance())) {
					if (fillListener != null) {
						fillListener.run();
						expirationTime.add(Calendar.SECOND, expirationSeconds);
					}
				}
			}
		}
	}
}
